/*****************************************************************************

INTEL CORPORATION PROPRIETARY INFORMATION
This software is supplied under the terms of a license agreement or
nondisclosure agreement with Intel Corporation and may not be copied
or disclosed except in accordance with the terms of that agreement.
Copyright(c) 2005-2014 Intel Corporation. All Rights Reserved.

*****************************************************************************/

#include "common_utils.h"

// ATTENTION: If D3D surfaces are used, DX9_D3D or DX11_D3D must be set in project settings or hardcoded here

#ifdef DX9_D3D
#include "common_directx.h"
#elif DX11_D3D
#include "common_directx11.h"
#endif

/* =======================================================
 * Windows implementation of OS-specific utility functions
 */


mfxU32 GetPreferredAdapterNum(const mfxAdaptersInfo & adapters, const bool bPrefferdGfx, const bool bPrefferiGfx)
{
	printf("GetPreferredAdapterNum(bPrefferdGfx=%d, bPrefferiGfx=%d\n", bPrefferdGfx, bPrefferiGfx);
	if (adapters.NumActual == 0 || !adapters.Adapters)
		return 0;

	if (bPrefferdGfx)
	{
		// Find dGfx adapter in list and return it's index

		auto idx = std::find_if(adapters.Adapters, adapters.Adapters + adapters.NumActual,
			[](const mfxAdapterInfo info)
		{
			return info.Platform.MediaAdapterType == mfxMediaAdapterType::MFX_MEDIA_DISCRETE;
		});

		// No dGfx in list
		if (idx == adapters.Adapters + adapters.NumActual)
		{
			printf("Warning: No dGfx detected on machine. Will pick another adapter\n");
			return 0;
		}

		printf("Success: dGfx detected on machine.\n");
		return static_cast<mfxU32>(std::distance(adapters.Adapters, idx));
	}

	if (bPrefferiGfx)
	{
		// Find iGfx adapter in list and return it's index

		auto idx = std::find_if(adapters.Adapters, adapters.Adapters + adapters.NumActual,
			[](const mfxAdapterInfo info)
		{
			return info.Platform.MediaAdapterType == mfxMediaAdapterType::MFX_MEDIA_INTEGRATED;
		});

		// No iGfx in list
		if (idx == adapters.Adapters + adapters.NumActual)
		{
			printf("Warning: No iGfx detected on machine. Will pick another adapter\n");
			return 0;
		}

		printf("Success: iGfx detected on machine.\n");
		return static_cast<mfxU32>(std::distance(adapters.Adapters, idx));
	}

	printf("GetPreferredAdapterNum: no iGfx/dGfx setting, return default value\n");
	// Other ways return 0, i.e. best suitable detected by dispatcher
	return 0;
}


mfxStatus external_LoadRawRGBAFrame(mfxMemId mid, FILE* fSource)
{
	mfxStatus sts = MFX_ERR_NONE;
	sts = internal_LoadRawRGBAFrame(fSource);
	if (MFX_ERR_NONE != sts)
	{
		return sts;
	}
	sts = internal_RGBA_NV12_VPP(mid);
	if (MFX_ERR_NONE != sts)
	{
		return sts;
	}

	return MFX_ERR_NONE;
}



mfxStatus external_CreateD3D11Device(mfxIMPL & impl, bool bPrefferdGfx, bool bPrefferiGfx )
{
	//先看一下输入参数，用户希望用集显和独显，再找到对应硬件的index创建设备

	mfxStatus sts = MFX_ERR_NONE;

	//根据bPrefferdGfx/bPrefferiGfx设置impl参数
	mfxU32 num_adapters_available;
	MFXQueryAdaptersNumber(&num_adapters_available);
	MSDK_CHECK_STATUS(sts, "MFXQueryAdaptersNumber failed");

	printf("num_adapters_available = %d\n\n", num_adapters_available);

	std::vector<mfxAdapterInfo> displays_data(num_adapters_available);
	mfxAdaptersInfo adapters = { displays_data.data(), mfxU32(displays_data.size()), 0u };

	sts = MFXQueryAdapters(NULL, &adapters);
	if (sts == MFX_ERR_NOT_FOUND)
	{
		printf("ERROR: No suitable adapters found for this workload\n");
	}
	MSDK_CHECK_STATUS(sts, "MFXQueryAdapters failed");

	mfxU32 idx = GetPreferredAdapterNum(adapters, bPrefferdGfx, bPrefferiGfx);
	mfxU32 nAdapterNum;
	nAdapterNum = 0;
	/* nAdapterNum对应创建d3d11 device的设备号 控制d3d11 device创建在集显还是独显上
	如果外面已经创建了d3d11 device, 则可以让impl = MFX_IMPL_HARDWARE_ANY,
	{MFX_IMPL_HARDWARE, 0},
	{ MFX_IMPL_SOFTWARE, 0 },
	{ MFX_IMPL_HARDWARE2, 1 },
	{ MFX_IMPL_HARDWARE3, 2 },
	{ MFX_IMPL_HARDWARE4, 3 }
	*/
	switch (adapters.Adapters[idx].Number)
	{
	case 0:
		impl = MFX_IMPL_HARDWARE;
		nAdapterNum = 0;
		printf("impl = MFX_IMPL_HARDWARE\n");
		break;
	case 1:
		impl = MFX_IMPL_HARDWARE2;
		nAdapterNum = 1;
		printf("impl = MFX_IMPL_HARDWARE2\n");
		break;
	case 2:
		impl = MFX_IMPL_HARDWARE3;
		nAdapterNum = 2;
		printf("impl = MFX_IMPL_HARDWARE3\n");
		break;
	case 3:
		impl = MFX_IMPL_HARDWARE4;
		nAdapterNum = 3;
		printf("impl = MFX_IMPL_HARDWARE4\n");
		break;

	default:
		// try searching on all display adapters
		impl = MFX_IMPL_HARDWARE_ANY;
		nAdapterNum = 0;
		printf("impl = MFX_IMPL_HARDWARE_ANY\n");
		break;
	}

	//这里先创建d3d11 device, 模拟外部已经创建了d3d11设备
//这里nAdapterNum控制d3d11 device创建在哪块intel显卡上，如果是外部创建的d3d11 device, 则硬件加速默认会跑在创建d3d11 device的显卡上
	sts = internal_CreateHWDevice(nAdapterNum);
	MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);

	return MFX_ERR_NONE;
}

mfxStatus external_CreateRGBAStagingTexture(mfxU16 width, mfxU16 height)
{
	return internal_CreateRGBAStagingTexture(width,height);
}

mfxStatus external_ReleaseRGBAStagingTexture()
{
	return internal_ReleaseRGBAStagingTexture();
}



mfxStatus Initialize(mfxIMPL impl, mfxVersion ver, MFXVideoSession* pSession, mfxFrameAllocator* pmfxAllocator, bool bCreateSharedHandles)
{
    mfxStatus sts = MFX_ERR_NONE;

#ifdef DX11_D3D
    impl |= MFX_IMPL_VIA_D3D11;
#endif

    // Initialize Intel Media SDK Session
    sts = pSession->Init(impl, &ver);
    MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);

#if defined(DX9_D3D) || defined(DX11_D3D)
    // If mfxFrameAllocator is provided it means we need to setup DirectX device and memory allocator
    if (pmfxAllocator) {
        // Create DirectX device context
        mfxHDL deviceHandle;

#ifdef ORIGINAL
		//这里是原始代码
		//现在需要剥离初始化d3d11 device的代码，因为已经在外面创建过d3d11 device了
        sts = CreateHWDevice(*pSession, &deviceHandle, NULL, bCreateSharedHandles);
        MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
#else
		//默认在外面d3d11设备创建成功 没有加判断
		//获取d3d11设备handle
		GetDeviceHandle(&deviceHandle);
#endif


        // Provide device manager to Media SDK
        sts = pSession->SetHandle(DEVICE_MGR_TYPE, deviceHandle);
        MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);

        pmfxAllocator->pthis  = *pSession; // We use Media SDK session ID as the allocation identifier
        pmfxAllocator->Alloc  = simple_alloc;
        pmfxAllocator->Free   = simple_free;
        pmfxAllocator->Lock   = simple_lock;
        pmfxAllocator->Unlock = simple_unlock;
        pmfxAllocator->GetHDL = simple_gethdl;

        // Since we are using video memory we must provide Media SDK with an external allocator
        sts = pSession->SetFrameAllocator(pmfxAllocator);
        MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
    }
#endif

    return sts;
}

void Release()
{
#if defined(DX9_D3D) || defined(DX11_D3D)
    CleanupHWDevice();
#endif
}

void mfxGetTime(mfxTime* timestamp)
{
    QueryPerformanceCounter(timestamp);
}

double TimeDiffMsec(mfxTime tfinish, mfxTime tstart)
{
    static LARGE_INTEGER tFreq = { 0 };

    if (!tFreq.QuadPart) QueryPerformanceFrequency(&tFreq);

    double freq = (double)tFreq.QuadPart;
    return 1000.0 * ((double)tfinish.QuadPart - (double)tstart.QuadPart) / freq;
}

void ClearYUVSurfaceVMem(mfxMemId memId)
{
#if defined(DX9_D3D) || defined(DX11_D3D)
    ClearYUVSurfaceD3D(memId);
#endif
}

void ClearRGBSurfaceVMem(mfxMemId memId)
{
#if defined(DX9_D3D) || defined(DX11_D3D)
    ClearRGBSurfaceD3D(memId);
#endif
}

